import numpy as np
import tkinter as tk
import sys
import os
from datetime import datetime
from concurrent.futures import ThreadPoolExecutor
import cv2
import PIL.Image
import PIL.ImageTk
from PIL import ImageGrab
import mediapipe as mp
import pyautogui
import math
from enum import IntEnum
import time
import threading
from google.protobuf.json_format import MessageToDict

pyautogui.FAILSAFE = False
mp_drawing = mp.solutions.drawing_utils
mp_hands = mp.solutions.hands

# Gesture Encodings
# 枚举类
class Gest(IntEnum):
    # FIST_MAJOR（右拳头）：0 ：00000
    # PINKY（小指）：1 ：00001
    # RING（无名指）：2 ：00010
    # MID（中指）：4 ：00100
    # LAST3（后三个手指）：7 ：00111
    # INDEX（食指）：8 ： 01000
    # FIRST2（前两个手指）：12 ： 01100
    # LAST4（后四个手指）：15 ： 01111
    # THUMB（大拇指）：16 ： 10000
    # PALM（手掌）：31 ： 11111

    # V_GEST（V手势）：33
    # TWO_FINGER_CLOSED（两指合拢）：34
    # PINCH_MAJOR（右手捏）：35
    # PINCH_MINOR（左手捏）：36
    FIST_MAJOR = 0
    PINKY = 1
    RING = 2
    MID = 4
    LAST3 = 7
    INDEX = 8
    FIRST2 = 12
    LAST4 = 15
    THUMB = 16
    PALM = 31

    V_GEST = 33
    TWO_FINGER_CLOSED = 34
    PINCH_MAJOR = 35
    PINCH_MINOR = 36
    FIST_MINOR = 37


class HLabel(IntEnum):
    # 标签用于指示左手和右手,在需要区分时进行标识和处理
    MINOR = 0
    MAJOR = 1


# 将手势识别与手部姿态估计相结合，通过处理手部关键点信息来计算手势编码并识别手势。
class HandRecog:
    # finger：表示手势对应的枚举类型Gest的值，存储当前帧的计算手势。
    # ori_gesture：表示手势对应的枚举类型Gest的值，存储正在使用的手势。
    # prev_gesture：表示手势对应的枚举类型Gest的值，存储上一帧计算的手势。
    # hand_result：从Mediapipe获取的Landmarks对象。
    # hand_label：表示多手性对应的枚举类型HLabel的值。

    def __init__(self, hand_label):

        self.finger = 0
        self.ori_gesture = Gest.PALM
        self.prev_gesture = Gest.PALM
        self.frame_count = 0
        self.hand_result = None
        self.hand_label = hand_label

    # 更新hand_result属性的值
    def update_hand_result(self, hand_result):
        self.hand_result = hand_result

    # 计算有符号距离
    def get_signed_dist(self, point):

        sign = -1
        if self.hand_result.landmark[point[0]].y < self.hand_result.landmark[point[1]].y:
            sign = 1
        dist = (self.hand_result.landmark[point[0]].x - self.hand_result.landmark[point[1]].x) ** 2
        dist += (self.hand_result.landmark[point[0]].y - self.hand_result.landmark[point[1]].y) ** 2
        dist = math.sqrt(dist)
        return dist * sign

    # 计算距离
    def get_dist(self, point):
        dist = (self.hand_result.landmark[point[0]].x - self.hand_result.landmark[point[1]].x) ** 2
        dist += (self.hand_result.landmark[point[0]].y - self.hand_result.landmark[point[1]].y) ** 2
        dist = math.sqrt(dist)
        return dist

    # 计算在z轴上的绝对差值
    def get_dz(self, point):
        return abs(self.hand_result.landmark[point[0]].z - self.hand_result.landmark[point[1]].z)

    # 对finger进行编码,标记弯曲的手指为0，伸直为1
    def set_finger_state(self):

        if self.hand_result == None:
            return

        points = [[8, 5, 0], [12, 9, 0], [16, 13, 0], [20, 17, 0]]  # 食指 中指 无名指 小拇指
        self.finger = 0
        self.finger = self.finger | 0  # thumb
        for idx, point in enumerate(points):

            dist = self.get_signed_dist(point[:2])  # 取数组前两个元素，关节两两之间的距离，dist为一根手指前两个关节距离
            dist2 = self.get_signed_dist(point[1:])     # 取数组从索引1开始的所有元素，即数组的后两个元素，dist2为一根手指后两个关节距离

            try:
                ratio = round(dist / dist2, 1)
            except:
                ratio = round(dist2 / 0.01, 1)

            self.finger = self.finger << 1  # 左移一位，为下一个手指的状态腾出一个位

            # 当手指伸直时，将finger的最后一位（对应当前手指）设置为1。
            if ratio > 0.5:  # 说明手指伸直
                self.finger = self.finger | 1
        # print(self.finger)

    # 根据finger的编码判断手势
    def get_gesture(self):

        if self.hand_result is None:
            return Gest.PALM
        current_gesture = Gest.PALM
        # 先判断大拇指是否打开且为食指打开
        if self.hand_result.landmark[0].x > self.hand_result.landmark[4].x+0.15 and self.finger in [8, 0]:
            if self.hand_result.landmark[8].y < self.hand_result.landmark[7].y:  # 食指完全展开
                threadpool.Magnify_Minify(1)
                # print(self.hand_result.landmark[8].y, self.hand_result.landmark[7].y)
                # print(self.hand_result.landmark[0].x, self.hand_result.landmark[4].x)
            else:
                threadpool.Magnify_Minify(0)
                # print(self.hand_result.landmark[8].y, self.hand_result.landmark[7].y)
            current_gesture = Gest.PALM
        else:
            # 判断 捏 手势
            if self.finger in [Gest.LAST3, Gest.LAST4] and self.get_dist([8, 4]) < 0.05:
                if self.hand_label == HLabel.MINOR:
                    current_gesture = Gest.PINCH_MINOR
                else:
                    current_gesture = Gest.PINCH_MAJOR

            # 判断拳头的左右手
            elif self.finger == 0:
                if self.hand_label == HLabel.MINOR:
                    current_gesture = Gest.FIST_MINOR
                else:
                    current_gesture = Gest.FIST_MAJOR

            elif Gest.FIRST2 == self.finger:  # 伸出食指和中指
                point = [[8, 12], [5, 9]]
                dist1 = self.get_dist(point[0])
                dist2 = self.get_dist(point[1])
                ratio = dist1 / dist2
                if ratio > 1.7:
                    current_gesture = Gest.V_GEST  # 伸出食指和中指的情况下，根据其距离识别为V手势、并拢手势、弯曲食指手势
                else:
                    if self.get_dz([8, 12]) < 0.1:
                        current_gesture = Gest.TWO_FINGER_CLOSED
                    else:
                        current_gesture = Gest.MID
            # 为8或PALM
            else:
                current_gesture = self.finger

        self.prev_gesture = current_gesture

        self.ori_gesture = current_gesture
        # print(self.ori_gesture)
        return self.ori_gesture


# 线程类，存放所有用到的线程
class ThreadPool:
    mouse_thread = None
    magmin_thread = None
    print_thread = None
    currentFrame = 0  # currentFrame和pre_x用来缩放记录时间
    pre_x = 0

    def __init__(self):
        ThreadPool.mouse_thread = ThreadPoolExecutor(max_workers=1)
        ThreadPool.magmin_thread = ThreadPoolExecutor(max_workers=1)
        ThreadPool.print_thread = ThreadPoolExecutor(max_workers=1)

    def Magnify_Minify(self, x):
        # print("线程控制缩放")
        if self.pre_x == x:     # 和上一帧相同缩放或放大手势
            self.currentFrame += 1
            if self.currentFrame > 15:
                self.currentFrame = 0   # currentFrame记录归零重新记录，防止操作过快
                ThreadPool.magmin_thread = threading.Timer(0, Controller.magmin, args=(x,)).start()
        else:
            self.pre_x = x

    # move_mouse_thread利用此线程完成对鼠标控制，与主进程分隔开
    def moveMouse(self, x, y):
        if x is not None and y is not None:
            if self.mouse_thread:
                self.mouse_thread.shutdown()  # 取消之前的线程，防止两个进程同时对鼠标控制导致抖动
                # print("取消线程的上一帧操作")
            self.mouse_thread = threading.Timer(0, pyautogui.moveTo, args=(x, y, 0.1)).start()

    def thread_print(self, text):
        self.print_thread = threading.Timer(0, print, args=(text,)).start()


# 控制器类，用于执行根据手势检测到的命令
class Controller:
    # tx_old 和 ty_old 是鼠标先前位置的 x 和 y 坐标。

    # flag 是一个布尔值，如果检测到 V 手势，则为 True。
    # grabflag 是一个布尔值，如果检测到拳头手势，则为 True
    # pinchmajorflag 是一个布尔值，如果通过 MAJOR 手掌检测到 PINCH 手势（沿 x 轴为 Controller.changesystembrightness，沿 y 轴为 Controller.changesystemvolume），则为 True。
    # pinchminorflag 是一个布尔值，如果通过 MINOR 手掌检测到 PINCH 手势（沿 x 轴为 Controller.scrollHorizontal，沿 y 轴为 Controller.scrollVertical），则为 True。
    # pinchstartxcoord 和 pinchstartycoord 是开始检测 PINCH 手势时手部标记的 x 和 y 坐标。
    # pinchdirectionflag 是一个布尔值，如果 PINCH 手势沿 x 轴移动，则为 True，否则为 False。

    # prevpinchlv 和 pinchlv 是从起始位置开始的 PINCH 手势位移的量化幅度。
    # framecount 是自更新 pinchlv 以来的帧数。
    # prev_hand 是前一帧中手部的 (x, y) 坐标。
    # pinch_threshold 是用于量化 pinchlv 的步长。
    # MouseToX, MouseToY是最终鼠标应该移动到的位置
    MouseToX, MouseToY = None, None

    tx_old = 0
    ty_old = 0
    trial = True
    flag = False
    grabflag = False
    majorfistflag = False
    minorfistflag = False
    # 为pinch功能实现
    pinchmajorflag = False
    pinchminorflag = False
    pinchstartxcoord = None
    pinchstartycoord = None
    pinchdirectionflag = None
    prevpinchlv = 0
    pinchlv = 0
    framecount = 0
    prev_hand = None
    pinch_threshold = 0.3
    scrolling = False
    magmined = False

    distance_multiple_factor = 1.5
    # 根据按钮更新
    v_threshold = 3
    Button2ChangeMoveStep = 1.1

    def __init__(self, Button2ChangeMoveStep):      # 接受gestureControl传过来的参数
        self.threadpool = ThreadPool()  # 实例化线程池
        self.Button2ChangeMoveStep = Button2ChangeMoveStep

    # 返回起始 PINCH y 坐标和当前手部位置 y 坐标之间的距离
    def getpinchylv(hand_result):
        dist = round((Controller.pinchstartycoord - hand_result.landmark[8].y) * 10, 1)
        return dist

    # 返回起始 PINCH x 坐标和当前手部位置 x 坐标之间的距离
    def getpinchxlv(hand_result):
        dist = round((hand_result.landmark[8].x - Controller.pinchstartxcoord) * 10, 1)
        return dist

    def ScreenShot(x):
        print("截图成功")
        # 获取可执行文件的路径
        exe_dir = os.path.dirname(sys.executable)
        # 创建截图文件夹
        screenshot_dir = os.path.join(exe_dir, "screenshot")
        os.makedirs(screenshot_dir, exist_ok=True)
        # 构建截图文件的保存路径
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        screenshot_path = os.path.join(screenshot_dir, f"screenshot_{timestamp}.png")
        # 进行截图并保存
        image = ImageGrab.grab()
        image.save(screenshot_path)

    def magmin(x):
        pyautogui.keyDown('ctrl')
        if x == 1:
            print("放大")
            pyautogui.scroll(120)
        else:
            print("缩小")
            pyautogui.scroll(-120)
        pyautogui.keyUp('ctrl')

    def Update(x):      # 在GestureController中点击按钮函数调用更新灵敏度
        Controller.Button2ChangeMoveStep = x

    def Updatev_threshold(x):      # 在GestureController中点击按钮函数调用更新防抖值
        Controller.v_threshold = x

    # 定位手部以获取鼠标指针位置，并通过减弱手部的抖动运动来稳定光标
    def get_position(hand_result):
        point = 9
        position = [hand_result.landmark[point].x, hand_result.landmark[point].y]
        sx, sy = pyautogui.size()
        x_old, y_old = pyautogui.position()

        x = 0.5 + (position[0] - 0.5)*Controller.Button2ChangeMoveStep      # 加大V手势鼠标移动范围，提高用户体验
        y = 0.5 + (position[1] - 0.5)*Controller.Button2ChangeMoveStep
        x = int(x * sx)
        y = int(y * sy)

        if Controller.prev_hand is None:
            Controller.prev_hand = x, y

        # 计算当前帧与前一帧的增量
        delta_x = x - Controller.prev_hand[0]
        delta_y = y - Controller.prev_hand[1]

        # print("dx:", delta_x, "dy", delta_y)
        if abs(delta_x) < Controller.v_threshold:
            delta_x = 0
            x = Controller.prev_hand[0]
        if abs(delta_y) < Controller.v_threshold:
            delta_y = 0
            y = Controller.prev_hand[1]

        # 应用平滑处理
        smoothed_delta_x = Controller.distance_multiple_factor * delta_x
        smoothed_delta_y = Controller.distance_multiple_factor * delta_y

        # 更新前一帧的位置
        Controller.prev_hand = [x, y]

        # 计算平滑后的位置
        smoothed_x = x_old + smoothed_delta_x
        smoothed_y = y_old + smoothed_delta_y

        return (x, y)

    # 为 PINCH 手势初始化属性
    def pinch_control_init(hand_result):
        # 根据 PINCH 标志和帧计数调用 controlHorizontal 或 controlVertical，并设置 pinchlv。
        # prevpinchlv上一帧的
        # pinchlv这一帧的
        Controller.pinchdirectionflag = None
        Controller.scrolling = False
        Controller.magmined = False
        Controller.pinchstartxcoord = hand_result.landmark[8].x
        Controller.pinchstartycoord = hand_result.landmark[8].y
        Controller.pinchlv = 0
        Controller.prevpinchlv = 0
        Controller.framecount = 0
        Controller.KeyDowned = False

    # 在屏幕上垂直滚动
    @staticmethod
    def scrollVertical():
        pyautogui.scroll(120 if Controller.pinchlv > 0.0 else -120)

    # 在屏幕上水平滚动
    @staticmethod
    def scrollHorizontal():
        pyautogui.keyDown('shift')
        pyautogui.keyDown('ctrl')
        pyautogui.scroll(-120 if Controller.pinchlv > 0.0 else 120)
        pyautogui.keyUp('ctrl')
        pyautogui.keyUp('shift')

    def pinch_minor_control(hand_result, controlHorizontal, controlVertical):
        Controller.pinchlv = Controller.prevpinchlv

        if Controller.pinchdirectionflag == True and Controller.scrolling == False:
            print("左右移动")
            controlHorizontal()  # x
            Controller.scrolling = True

        elif Controller.pinchdirectionflag == False and Controller.scrolling == False:
            print("上下移动")
            controlVertical()  # y
            Controller.scrolling = True

        lvx = Controller.getpinchxlv(hand_result)   # 当前手与初始位置的x距离
        lvy = Controller.getpinchylv(hand_result)   # 当前手与初始位置的y距离
        # y方向上的移动大于x方向，且y方向移动幅度大于阈值
        if abs(lvy) > abs(lvx) and abs(lvy) > Controller.pinch_threshold:
            Controller.pinchdirectionflag = False
            if abs(Controller.prevpinchlv - lvy) >= Controller.pinch_threshold:
                Controller.prevpinchlv = lvy
            else:
                Controller.framecount += 1
            if Controller.framecount >= 5:
                Controller.framecount = 0
                # print("持续上下移动中")
                controlVertical()  # y

        elif abs(lvx) > Controller.pinch_threshold:
            Controller.pinchdirectionflag = True
            if abs(Controller.prevpinchlv - lvx) >= Controller.pinch_threshold:
                Controller.prevpinchlv = lvx
            else:
                Controller.framecount += 1
            if Controller.framecount >= 5:
                Controller.framecount = 0
                controlHorizontal()  # x

    # 实现所有手势功能
    def handle_controls(gesture, hand_result):
        MouseToX, MouseToY = None, None
        # print(gesture)
        if gesture != Gest.PALM:
            MouseToX, MouseToY = Controller.get_position(hand_result)

        # 根据当前手势类型重置标志位 手掌
        if gesture != Gest.FIST_MAJOR and Controller.majorfistflag:
            Controller.majorfistflag = False
            threadpool.moveMouse(MouseToX, MouseToY)
            pyautogui.mouseUp(button="left")

        # 当当前手势不为FIST_MINOR左手握拳且minorfistflag为True，则设置为false方便下一次左手握拳使用
        if gesture != Gest.FIST_MINOR and Controller.minorfistflag:
            Controller.minorfistflag = False

        if gesture != Gest.PINCH_MAJOR and Controller.pinchmajorflag:
            Controller.pinchmajorflag = False

        if gesture != Gest.PINCH_MINOR and Controller.pinchminorflag:
            Controller.pinchminorflag = False

        # 手势控制响应函数的实现
        # V,控制鼠标，将鼠标移动到指定位置
        if gesture == Gest.V_GEST:
            Controller.flag = True
            threadpool.moveMouse(MouseToX, MouseToY)

        # 拳 实现长按左键以执行拖放操作
        elif gesture == Gest.FIST_MAJOR:
            if not Controller.majorfistflag:
                print("拖动中")
                Controller.majorfistflag = True
                pyautogui.mouseDown(button="left")
            threadpool.moveMouse(MouseToX, MouseToY)

        # 当前手势为左手握拳第一帧，执行截图操作并Controller.minorfistflag = True防止重复截图
        elif gesture == Gest.FIST_MINOR:
            if not Controller.minorfistflag:
                Controller.minorfistflag = True
                Controller.ScreenShot(None)

        # 弯曲食指
        elif gesture == Gest.MID and Controller.flag:
            pyautogui.click()
            Controller.flag = False

        # 弯曲中指
        elif gesture == Gest.INDEX and Controller.flag:
            pyautogui.click(button='right')
            Controller.flag = False

        # 合并食指和中指
        elif gesture == Gest.TWO_FINGER_CLOSED and Controller.flag:
            print("双击")
            pyautogui.doubleClick()
            Controller.flag = False

        # 捏
        elif gesture == Gest.PINCH_MINOR:
            if Controller.pinchminorflag == False:  # 第一次出现pinch手势，需要初始化
                Controller.pinch_control_init(hand_result)
                Controller.pinchminorflag = True
            Controller.pinch_minor_control(hand_result, Controller.scrollHorizontal, Controller.scrollVertical)

        dic = {
            0:"拳头",#00000
            1:"小指",#00001
            2:"无名指",#00010
            4:"中指",#00100
            7:"后三个手指",#00111
            8:"食指",#01000
            12:"前两个手指",#01100
            15:"后四个手指",#01111
            16:"大拇指",#10000
            31:"手掌",#11111
            33:"V",
            34:"两指合拢",
            35:"PINCH_MAJOR",
            36:"PINCH_MINOR"
        }
        # print(gesture)
        # if gesture in dic:
        #     print(dic[gesture])


# main
# 实现了手势的识别和控制
class GestureController:
    # gc_mode：表示手势控制器的模式。
    # cap：用于捕获视频帧的cv2.VideoCapture对象。
    # CAM_HEIGHT和CAM_WIDTH：相机的高度和宽度。
    # hr_major和hr_minor：分别代表主要手和次要手的手势。
    # dom_hand：布尔值，表示是否使用主要手作为主要手势。
    gc_mode = 0
    cap = None
    CAM_HEIGHT = None
    CAM_WIDTH = None
    hr_major = None  # Right Hand by default
    hr_minor = None  # Left hand by default
    dom_hand = True
    controller = None

    imageGui = None  # imageGui变量在Update_Video和start()都用
    results = None  # results变量在Update_Video和start()都用
    # 以下为按钮的实现而存在的变量
    Button2ChangeMoveStep = 1.4  # 鼠标灵敏度--Button2来控制鼠标灵敏度移动大小
    v_threshold = 3     # 默认抖动阈值--
    FPS_IsOpen = False      # 默认FPS打开与否
    show_image = False   # 默认说明图打开与否
    ShowMode = 0        # 默认显示模式为0是只显示手部点

    # 防抖度控制按钮
    def button1_clicked(self):
        current_color = self.function_frame.grid_slaves(row=0, column=0)[0].cget('bg')      # 获取按钮一背景颜色
        if current_color == "white":
            new_color = "light green"
            self.v_threshold = 5
            print(f"当前防抖度为：中")
            Controller.Updatev_threshold(self.v_threshold)  # 更新Controller中的Button2ChangeMoveStep值
        elif current_color == "light green":
            new_color = "green"
            self.v_threshold = 7
            print(f"当前防抖度为：高")
            Controller.Updatev_threshold(self.v_threshold)  # 更新Controller中的Button2ChangeMoveStep值
        else:
            new_color = "white"
            self.v_threshold = 3
            print(f"当前防抖度为：低")
            Controller.Updatev_threshold(self.v_threshold)  # 更新Controller中的Button2ChangeMoveStep值
        self.function_frame.grid_slaves(row=0, column=0)[0].configure(bg=new_color)

    # 灵敏度按钮调用函数
    def button2_clicked(self):
        current_color = self.function_frame.grid_slaves(row=1, column=0)[0].cget('bg')      # 获取按钮二背景颜色
        if current_color == "white":
            new_color = "light green"
            self.Button2ChangeMoveStep = 1.5
            print(f"当前灵敏度为：中")
            Controller.Update(self.Button2ChangeMoveStep)  # 更新Controller中的Button2ChangeMoveStep值
        elif current_color == "light green":
            new_color = "green"
            self.Button2ChangeMoveStep = 1.6
            print(f"当前灵敏度为：高")
            Controller.Update(self.Button2ChangeMoveStep)  # 更新Controller中的Button2ChangeMoveStep值
        else:
            new_color = "white"
            self.Button2ChangeMoveStep = 1.4
            print(f"当前灵敏度为：低")
            Controller.Update(self.Button2ChangeMoveStep)  # 更新Controller中的Button2ChangeMoveStep值
        self.function_frame.grid_slaves(row=1, column=0)[0].configure(bg=new_color)

    def button3_clicked(self):
        current_color = self.function_frame.grid_slaves(row=2, column=0)[0].cget('bg')      # 获取按钮三背景颜色
        if current_color == "green":
            new_color = "white"
            self.FPS_IsOpen = False
            print("关闭FPS显示")
        else:
            new_color = "green"
            self.FPS_IsOpen = True
            print("打开FPS显示")
        self.function_frame.grid_slaves(row=2, column=0)[0].configure(bg=new_color)

    def button4_clicked(self):
        if self.show_image:
            new_color = "white"
            self.image_label.destroy()  # 移除图片标签
            self.show_image = False
        else:
            new_color = "green"
            image = PIL.Image.open("description.jpg")
            resized_image = image.resize((640, 480))
            self.image_photo = PIL.ImageTk.PhotoImage(resized_image)
            self.image_label = tk.Label(self.camera_frame, image=self.image_photo)
            self.image_label.place(x=0, y=0)
            self.show_image = True
        self.function_frame.grid_slaves(row=3, column=0)[0].configure(bg=new_color)

    def button5_clicked(self):
        if self.ShowMode == 0:
            self.ShowMode = 1
            print("显示实时图像")
        else:
            self.ShowMode = 0
            print("显示手部关键点")

    # 根据传入的手势结果来对主要手和次要手进行分类，并将分类结果保存在hr_major和hr_minor属性中
    def classify_hands(results):
        # 这段代码用于分类识别左手和右手，并将它们分配给GestureController.hr_major和GestureController.hr_minor对象。
        left, right = None, None
        try:
            handedness_dict = MessageToDict(results.multi_handedness[0])
            if handedness_dict['classification'][0]['label'] == 'Right':
                right = results.multi_hand_landmarks[0]
            else:
                left = results.multi_hand_landmarks[0]
        except:
            pass

        try:
            handedness_dict = MessageToDict(results.multi_handedness[1])
            if handedness_dict['classification'][0]['label'] == 'Right':
                # results.multi_hand_landmarks[1]表示检测到的第二只手的关键点信息。
                right = results.multi_hand_landmarks[1]
            else:
                left = results.multi_hand_landmarks[1]
        except:
            pass

        if GestureController.dom_hand == True:
            GestureController.hr_major = right
            GestureController.hr_minor = left
        else:
            GestureController.hr_major = left
            GestureController.hr_minor = right

    def write(self, info):
        # info信息即标准输出sys.stdout和sys.stderr接收到的输出信息
        self.t.insert('end', info)
        self.t.update()  # 更新显示的文本
        self.t.see(tk.END)  # 始终显示最后一行

    def restoreStd(self):
        sys.stdout = self.stdoutbak
        sys.stderr = self.stderrbak

    def __init__(self, root):
        """Initilaizes attributes."""
        GestureController.gc_mode = 1
        GestureController.cap = cv2.VideoCapture(0)
        GestureController.cap.set(cv2.CAP_PROP_FPS, 1)  # 设置帧率
        # 设置界面位置
        if True:
            self.root = root
            self.root.geometry('1280x840')
            self.root.title('星期八加班队')
            self.imageGui = None
            # 创建背景显示区域
            self.background_frame = tk.Frame(self.root)
            self.background_frame.place(x=0, y=0)

            # 显示背景图片
            self.background_image = PIL.Image.open('background.png')
            self.background_photo = PIL.ImageTk.PhotoImage(self.background_image)
            self.background_label = tk.Label(self.background_frame, image=self.background_photo)
            self.background_label.pack()

            # 创建功能区域
            self.function_frame = tk.Frame(self.root, width=250, height=700)
            # self.function_frame.configure(background='deep sky blue')
            self.function_frame.place(x=20, y=250)

            # 一、创建按钮1
            button1 = tk.Button(self.function_frame, text="防抖性", width=20, height=2, command=self.button1_clicked)
            button1.grid(row=0, column=0, padx=10, pady=25, sticky="nsew")

            # 二、创建按钮2
            button2 = tk.Button(self.function_frame, text="灵敏度", width=20, height=2, command=self.button2_clicked)
            button2.grid(row=1, column=0, padx=10, pady=25)

            # 三、创建按钮3
            button3 = tk.Button(self.function_frame, text="FPS", width=20, height=2, command=self.button3_clicked)
            button3.grid(row=2, column=0, padx=0, pady=25)

            # 四、创建按钮4
            button4 = tk.Button(self.function_frame, text="使用说明", width=20, height=2, command=self.button4_clicked)
            button4.grid(row=3, column=0, padx=10, pady=25)

            # 五、创建按钮5
            button4 = tk.Button(self.function_frame, text="显示模式", width=20, height=2, command=self.button5_clicked)
            button4.grid(row=4, column=0, padx=10, pady=25)

            # 创建摄像头显示区域
            self.camera_frame = tk.Frame(self.root, width=640, height=480)
            self.camera_frame.place(x=320, y=185)

            # 创建反馈区
            self.feedback_frame = tk.Frame(self.root, width=10, height=50)
            self.feedback_frame.place(x=1000, y=185)

            # 将其备份
            self.stdoutbak = sys.stdout
            self.stderrbak = sys.stderr

            # 重定向
            sys.stdout = self
            sys.stderr = self
            # 创建带有背景图片的文本框
            self.t = tk.Text(self.feedback_frame, width=26, height=30)

            self.t.pack()

            # 在摄像头显示区域显示摄像头画面
            self.camera_label = tk.Label(self.camera_frame)
            self.camera_label.pack()

    def Update_Video(self, camera_label, imageGui, results):
        # 使用 GestureController传过来的camera_label
        if self.ShowMode == 0:
            back_image = PIL.Image.open('BackImage.png')
            imageGui = np.array(back_image)

        if imageGui is not None:
            # 在向imageGui用draw_landmarks画手部点先将imageGui类型转化为BGR
            imageGui = cv2.cvtColor(imageGui, cv2.COLOR_RGB2BGR)
            if results is not None:
                if results.multi_hand_landmarks:
                    for hand_landmarks in results.multi_hand_landmarks:
                        mp_drawing.draw_landmarks(imageGui, hand_landmarks, mp_hands.HAND_CONNECTIONS)

            imageGui = cv2.cvtColor(imageGui, cv2.COLOR_BGR2RGB)
            # imageGui不为空,并传送给GUI显示
            imageGui = PIL.Image.fromarray(imageGui)
            imageGui = imageGui.resize((640, 480), PIL.Image.LANCZOS)
            photo = PIL.ImageTk.PhotoImage(imageGui)
            camera_label.configure(image=photo)
            camera_label.image = photo

        # 在10ms后再次调用自身
        root.after(10, GestureController.Update_Video, self, self.camera_label, self.imageGui, self.results)

    # 整个程序的入口点。
    # 循环读取视频帧，并使用MediaPipe库提取手势的关键点。
    # 然后将关键点传递给handmajor和handminor对象进行手势控制。
    # 根据手势的类型，调用Controller.handle_controls()方法来处理手势的控制操作
    def start(self):
        pTime = 0
        handmajor = HandRecog(HLabel.MAJOR)
        handminor = HandRecog(HLabel.MINOR)

        # 创建一个新的线程来运行Update_Video方法
        video_thread = threading.Thread(target=GestureController.Update_Video,
                                        args=(self, self.camera_label, self.imageGui, self.results))
        video_thread.start()

        with mp_hands.Hands(max_num_hands=2, min_detection_confidence=0.5, min_tracking_confidence=0.5) as hands:
            while GestureController.cap.isOpened() and GestureController.gc_mode:
                success, image = GestureController.cap.read()
                if not success:
                    print("Ignoring empty camera frame.")
                    continue
                image = cv2.cvtColor(cv2.flip(image, 1), cv2.COLOR_BGR2RGB)
                # imageGui、image都是GestureController.cap.read()实时图片信息，imageGui用于显示界面，image用于mediapipe手势识别
                self.imageGui = image

                image.flags.writeable = False
                self.results = hands.process(image)
                image.flags.writeable = True

                # 对mediapipe库返回的所有手部信息进行相关处理
                if self.results.multi_hand_landmarks:
                    GestureController.classify_hands(self.results)
                    handmajor.update_hand_result(GestureController.hr_major)
                    handminor.update_hand_result(GestureController.hr_minor)

                    handmajor.set_finger_state()
                    handminor.set_finger_state()
                    # 通过handminor.get_gesture()得到gest_name即当前手势
                    gest_name = handminor.get_gesture()

                    if gest_name in [Gest.PINCH_MINOR, Gest.FIST_MINOR]:
                        Controller.handle_controls(gest_name, handminor.hand_result)
                    else:
                        gest_name = handmajor.get_gesture()
                        Controller.handle_controls(gest_name, handmajor.hand_result)
                else:
                    Controller.prev_hand = None

                if self.FPS_IsOpen:
                    cTime = time.time()
                    fps = 1 / (cTime - pTime)
                    pTime = cTime
                    # print(f'FPS: {int(fps)}')
                    threadpool.thread_print(f'FPS: {int(fps)}')

                if GestureController.gc_mode != 1:
                    break

        GestureController.cap.release()
        cv2.destroyAllWindows()


# 创建一个函数，在其中执行 gc.start()
def start_gesture_controller(gc):
    gc.start()


def start_Gui(a):
    while True:
        print(gc.ShowMode)


def on_closing():
    gc.cap.release()
    cv2.destroyAllWindows()
    # 关闭GUI窗口
    root.destroy()

# 创建GUI窗口
root = tk.Tk()

threadpool = ThreadPool()
gc = GestureController(root)

# 创建一个线程来运行 gc.start()
gc_thread = threading.Thread(target=start_gesture_controller, args=(gc,))
gc_thread.start()

# 设置窗口关闭时的回调函数
root.protocol("WM_DELETE_WINDOW", on_closing)

# 创建一个线程来运行 Gui.start()
gui_thread = threading.Thread(target=start_Gui, args=(gc,))
# gui_thread.start()

root.mainloop()



